package com.zkq.graduate.conf;

import com.zkq.graduate.service.MqService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;

/**
 * @Author zhangkaiqiang
 * @Date 2019/9/17  18:39
 * @Description TODO
 */
@Configuration
@Slf4j
public class RabbitMqConfig {

	private final static String QUEUE_NAME = "graduate-message";
	private final static String EXCHANGE_NAME = "graduate-message-exchange";

	@Resource
	private CachingConnectionFactory connectionFactory;;


	@Resource(name = "mqService")
	private MqService mqService;

	/**
	 * 定义队列
	 * @return
	 */
	@Bean(name = "message")
	public Queue myMessageQueue() {
		/**
		 * durable 为true时server重启队列不会消失 (是否持久化)
		 * exclusive 队列是否是独占的，如果为true只能被一个connection使用，其他连接建立时会抛出异常
		 *autoDelete 当没有任何消费者使用时，自动删除该队列
		 */
		return new Queue(QUEUE_NAME,false,false,true);
	}

	/**
	 * 定义交换器
	 * @return
	 */
     @Bean
	public TopicExchange exchange(){
		return new TopicExchange(EXCHANGE_NAME);
	}

	/**
	 * 绑定交换器与队列
	 * @return
	 */
     @Bean
	public Binding bindingExchangeAndFirstQueue(@Qualifier("message") Queue queue, TopicExchange exchange){
     	return BindingBuilder.bind(queue).to(exchange).with("send.message");
	 }

//	 @Bean
//	 public MessageConverter messageConverter(){
//     	return new Jackson2JsonMessageConverter();
//	 }


	/**
	 *设置单例的RabbitTemplate，因为setConfirmCallback()和setReturnCallback()里的接口只能调用一遍
	 * 需要设置为单例或者设置为多列，每次设置不同的方法
	 * @return RabbitTemplate
	 */
	@Bean
	public RabbitTemplate singleRabbitTemplate(){
		RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
		rabbitTemplate.setMandatory(true);
		// 消息成功发送到Exchange或者发送到Exchange失败会调用该方法
		rabbitTemplate.setConfirmCallback((CorrelationData correlationData, boolean ack, String cause)->{
			if(ack){
				log.info(correlationData.getId()+">>>>>>消息发送成功");
			}else{
				log.info(correlationData.getId()+ ">>>>>发送失败,失败原因为:" + cause);

				// 此处应该吧发送失败的消息写入到表中,通过定时任务进行重试操作
			}
		});

		// 消息从Exchange路由到Queue失败时会调用该方法,进行重发操作
		rabbitTemplate.setReturnCallback((Message message, int replyCode, String replyText, String exchange, String routingKey)->{
			log.info("消息从Exchange路由到Queue失败,进行重发操作,消息回复代码为:"+replyCode+",routingKey为:"+routingKey+",exchange为:"+exchange);
			String content=new String(message.getBody());
			mqService.publish(content);
		});
		return rabbitTemplate;
	}
}
